<?php

/**
 * Dat@ctiv Framework
 *
 * @author		Mathieu Rey
 * @copyright	Copyright (c) 2008, datactiv.fr
 * @link		http://www.datactiv.fr
 */

/**
 * Forms
 *
 * Cette classe a pour but de simplifier l'exploitation des formulaires HTML
 *
 * @author		Mathieu Rey
 */

/**
 *                     TODO
 *
 * - controle présence du fichier de spécifications
 * - ecrire le token avec form()
 * - trace dans le log, et erreurs plus explicites si qque chose manque (fichier de defs, ou seulement une def dans le fichier)
 * - ressortir l'attribut "required" du tableau "attributs" (verifier gestion JS des radio/checkboxes) 
 */

/*
// ------------------

error_reporting("E^ALL");
set_error_handler('_exception_handler');

function _exception_handler($severity, $message, $filepath, $line){
	echo "<br /><b>".$severity." : </b> ".$message." (fichier : ".$filepath.", ligne : ".$line.")<br /><br />";
}

// ------------------
*/
class Forms{


	protected $separateur = "<br />";		// Séparateur entre le label et le champ
	protected $separateurcr = "<br />";		// Séparateur entre éléments radio / checkbox
	protected $separateurentre = "<br /><br />";		// Séparateur entre deux entrées

	protected $d = array();					// Données pour peupler le formulaire

	protected $s = array();					// Spécifications du formulaire

	protected $erreurs = "";				// Erreurs éventuelles suite à la vérification


	function __construct(){

	}

	/**
	 * setSpecifications
	 * 
	 * Lie le fichier de spécifications
	 */
	function setSpecifications($specif){
		
		$specif = strtolower($specif);

		GLOBAL $PATHBASE;
		if (!file_exists($PATHBASE."views/".$specif."_form.php"))
			die("fichier d'aide aux formulaires inexistant");

		include($PATHBASE."views/".$specif."_form.php");


		$this->s =& $champ;
		
	}
	
	/**
	 * setData
	 * 
	 * Fournit les données à réafficher dans le formulaire
	 */
	function setData ($source){

		$this->d =& $source;

	}

	/**
	 * setSeparateurs
	 * 
	 * Permer de modifier les séparateurs par défaut
	 */
	function setSeparateurs($s1, $s2, $s3){

		$this->separateur = $s1;
		$this->separateurcr = $s2;
		$this->separateurentre = $s3;

	}

	/**
	 * testerRegles
	 *
	 * Vérifie la validité des entrées selon les règles établies
	 */
	function testerRegles($donnees){

		$errs = "";

		foreach ($this->s as $k => $v){
			// $k contient le nom du champ de formulaire
			if (isset($v["required"])){
				if (!isset($donnees[$k]) || $donnees[$k] == ""){
					// Problème !
					$errs .= "Le champ '".$v["nom"]."' est requis.\n";
					continue; // Passe au champ suivant
				}
			}
			if ($donnees[$k] == "")
				continue; // Le champ n'est pas requis, mais ce n'est pas une raison pour tester du vide !!!
			if (!isset($v["attributs"]) || $v["attributs"] == "")
				continue; // Dans ce cas, c'est qu'il n'y a rien à tester

			// Quand on arrive ici, on a une variable non nulle, et au moins un attribut
			foreach($v["attributs"] as $c => $d){

				$masque = "";

				switch(strtolower($c)){

					case "minlength":
						// Longueur minimale

						if (strlen($donnees[$k]) < $d){
							// Trop court
							$errs .= "La valeur de '".$v["nom"]."' est trop courte (minimum ".$d." caractères)\n";
							break(2); // Sort du switch et du foreach sur les attributs (passe à l'attribut suivant)
						}

					break;

					case "maxlength":
						// Longueur maximale

						if (strlen($donnees[$k]) > $d){
							// Trop long
							$errs .= "La valeur de '".$v["nom"]."' est trop longue (maximum ".$d." caractères)\n";
							break(2); // Sort du switch et du foreach sur les attributs (passe à l'attribut suivant)
						}

					break;

					case "mask":
						// numeric, letter, domain, email
						$domainString='(com|org|net|mil|edu|info|mobi|travel|a[cdfgilmnoqrstuwxz]|b[abdefghijmnorstwyz]|c[acdfghiklmnoruvxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[adefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnrwyz]|l[abcikrstuvy]|m[acdghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eouw]|s[abcdeghiklmnrtvyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[etu]|z[amw])';

						switch($d){

							case "numeric":
								$masque = "/^[0-9]+$/i";
							break;

							case "letter":
								$masque = "/^[A-Zæøå]+$/i";
							break;

							case "domain":
								$masque = "/^(https?\:\/\/)?[a-zA-Z0-9]+([a-zA-Z0-9\-\.]+)?\\.".$domainString."$/i";
							break;

							case "email":
								$masque = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.".$domainString."$/i";
							break;

						}

						if ($this->testregex($masque, $donnees[$k]) != true){
							// Problème
							$errs .= "Le champ '".$v["nom"]."' ne respecte pas le format demandé\n";
							break(2);
						}

					break;

					case "freemask":
						// S pour une lettre majuscule, s pour une minuscule, N pour un chiffre

						$masque = "/^".str_replace(array("s", "S", "N"),
									array("[a-z]", "[A-Z]", "[0-9]"),
									$d)."$/";

						if (isset($v["attributs"]["caseInsensitive"])) // Insensible à la casse
							$masque .= "i";

							

						if ($this->testregex($masque, $donnees[$k]) != true){
							// Problème
							$errs .= "Le champ '".$v["nom"]."' ne respecte pas le format demandé\n";
							break(2);
						}


					break;

					case "regexppattern":
						// une vraie regex toute prête !

						$masque = "/".$d."/";

						

						if ($this->testregex($masque, $donnees[$k]) != true){
							// Problème
							$errs .= "Le champ '".$v["nom"]."' ne respecte pas le format demandé\n";
							break(2);
						}

					break;

					case "equalto":
						// Egal à un autre champ

						if (isset($donnees[$d]) && $donnees[$d] != "" && isset($donnees[$k]) && $donnees[$k] != "" && $donnees[$k] == $donnees[$d]){ // Même valeur non nulle, c'est OK
								break(2);
						}elseif ((!isset($donnees[$d]) || $donnees[$d] == "") && (!isset($donnees[$k]) || $donnees[$k] == "")){ // Vides tous les deux, c'est OK
								break(2);
						}else{
							// Erreur
							$errs .= "Le champ '".$v["nom"]."' doit être identique au champ '".$this->s[$d]["nom"]."'\n";
							break(2); // Pour passer au champ de formulaire suivant
						}

					break;

				}

			}

		}

		if (strlen($errs) > 0){ // On a des erreurs
			$this->erreurs =$errs;
			return false;
		}else // Pas de problème
			return true;

	}

	function affichererreurs(){
		if ($this->erreurs != ""){
			echo '<div style="margin: 15px; padding: 15px; background-color: red; color: white; width: 50%;"><b>Erreur !</b><br /><br />'.nl2br($this->erreurs).'</div>';
		}
	}


	/**
	 * testregex
	 * 
	 * Teste si l'expression régulière est vérifiée
	 */
	function testregex($regex, $val){

		// DEBUG
		//echo "regex : ".$regex."<br />chaine : ".$val."<br />réponse : ".preg_match($regex, $val)."<br />";
		return preg_match($regex, $val);


	}


	/**
	 * form
	 * 
	 * Ecrit la balise de début du formulaire, et inscrit le token
	 */
	function form($action){

		echo '<form action="'.$action.'" method="POST">';

		// Ecrire le token
		// TODO

	}

	/**
	 * entree
	 * 
	 * Affiche un champ de formulaire formaté suivant les spécifications
	 */
	function entree($id){
		
		if (!isset($this->s[$id])){ 

			// Le champ est inconnu, on l'écrit discrètement sur la page (commentaire)
			echo "<!-- Champ '".$id."' inconnu -->";

			// Laisse une trace dans le log
			// TODO

			return;
		}

		switch($this->s[$id]["type"]){

			case "input":
			case "textarea":
			case "select":
			case "password":

				// Elements "simples", affichage direct du label
				echo '<label for="'.$id.'">'.$this->s[$id]["nom"];
				echo (isset($this->s[$id]["required"]) ? " *" : "");
				echo '</label>'.$this->separateur;

				if ($this->s[$id]["type"] == "input"){

					// Affichage de l'élément
					echo '<input type="text" name="'.$id.'" id="'.$id.'"';

					
					// Regarde si c'est requis ou pas
					echo ((isset($this->s[$id]["required"])) ? ' required="required" ' : "");

					// Ajoute les propriétés
					if (isset($this->s[$id]["attributs"]))
						foreach ($this->s[$id]["attributs"] as $k => $v)
							echo ' '.$k.'="'.$v.'"';

					// Si on a une valeur précédente, on la réaffiche
					// Sinon, on regarde si on doit afficher une valeur par défaut
					if (isset($this->d[$id]) && $this->d[$id] != "")
						echo ' value="'.$this->d[$id].'"';
					elseif (isset($this->s[$id]["default"]))
						echo ' value="'.$this->s[$id]["default"].'"';

					echo ' />';

				}elseif ($this->s[$id]["type"] == "password"){

					// Affichage de l'élément
					echo '<input type="password" name="'.$id.'" id="'.$id.'"';

					
					// Regarde si c'est requis ou pas
					echo ((isset($this->s[$id]["required"])) ? ' required="required" ' : "");

					// Ajoute les propriétés
					if (isset($this->s[$id]["attributs"]))
						foreach ($this->s[$id]["attributs"] as $k => $v)
							echo ' '.$k.'="'.$v.'"';

					// Remarque
					// --------
					// Pour des raisons de confidentialité, on ne reprend pas la valeur entrée pour les champs "password"

					echo ' />';

				}elseif ($this->s[$id]["type"] == "textarea"){

					// Affichage de l'élément
					echo '<textarea name="'.$id.'" id="'.$id.'"';

					
					// Regarde si c'est requis ou pas
					echo ((isset($this->s[$id]["required"])) ? ' required="required" ' : "");

					// Ajoute les propriétés
					if (isset($this->s[$id]["attributs"]))
						foreach ($this->s[$id]["attributs"] as $k => $v)
							echo ' '.$k.'="'.$v.'"';
					echo '>';
					
					// Si on a une valeur précédente, on la réaffiche
					// Sinon, on regarde si on doit afficher une valeur par défaut
					if (isset($this->d[$id]) && $this->d[$id] != "")
						echo $this->d[$id];
					elseif (isset($this->s[$id]["default"]))
						echo $this->s[$id]["default"];

					echo '</textarea>';

				}else{ 
					// -> select
					// ---------

					// Affichage de l'élément
					echo '<select name="'.$id.'" id="'.$id.'"';

					// Ajoute les propriétés
					if (isset($this->s[$id]["attributs"]))
						foreach ($this->s[$id]["attributs"] as $k => $v)
							echo ' '.$k.'="'.$v.'"';
					
					// Regarde si c'est requis ou pas
					echo ((isset($this->s[$id]["required"])) ? ' required="required" ' : "");

					// Si on n'a pas de précision sur la taille, affiche un combobox (hauteur = 1)
					if (!isset($this->s[$id]["attributs"]["size"])) 
						echo ' size="1"';

					echo '>';

					// Affichage des éléments
					if (isset($this->s[$id]["valeurs"])){

						// Définition de la valeur sélectionnée par défaut
						if (isset($this->d[$id]) && $this->d[$id] != ""){
							// On a une valeur de retour
							$selectionne = $this->d[$id];
						}elseif (isset($this->s[$id]["default"])){
							// On a une valeur par défaut
							$selectionne = $this->s[$id]["default"];
						}else
							$selectionne = "";

						// Remarque importante
						// -------------------
						// Avec cette méthode, on ne prend pas en charge le retour de plus d'une valeur sélectionnée 
						// (cela peut arriver si size > 1, en sélectionnant plusieurs entrées avec la touche Ctrl)

						// Affichage
						foreach ($this->s[$id]["valeurs"] as $k => $v){
							if (isset($v)) {
								// On a un tableau associatif
								echo '<option value="'.$k.'"';
								echo (($selectionne == $k) ? ' selected="selected"' : '');
								echo '>'.$v.'</option>';
							}else{
								// Tableau simple
								echo '<option';
								echo (($selectionne == $k) ? ' selected="selected"' : '');
								echo '>'.$k.'</option>';
							}

						}
					}

					echo '</select>';

				}

			break;

			case "checkbox":
			case "radio":

				// Elements "composés", crée un fieldset et affiche un legend
				echo '<fieldset><legend>'.$this->s[$id]["nom"];
				echo (isset($this->s[$id]["required"]) ? " *" : "");
				echo '</legend>';

				// Affichage des éléments
				if ($this->s[$id]["type"] == "checkbox"){

					// -> checkboxes
					// -------------

					// On liste les cases qu'il faudra cocher selon les données reçues
					$selectionne = array();
					if (isset($this->d[$id])){
						foreach($this->d[$id] as $k)
							$selectionne[$k] = true;
					}

					if (isset($this->s[$id]["valeurs"])){
						foreach ($this->s[$id]["valeurs"] as $k => $v){
							echo '<input type="checkbox" name="'.$id.'[]" id="'.$k.'" value="'.$k.'"';

							// Regarde si c'est requis ou pas
							echo ((isset($this->s[$id]["required"])) ? ' required="required" ' : "");
							
							// Teste si la case doit être cochée ou non
							echo ((isset($selectionne[$k]) && $selectionne[$k] == true) ? ' checked="checked"' : '');
							
							echo '/> <label for="'.$k.'"> '.$v.'</label>';
							echo $this->separateurcr;
						}
					}

				}else{ 
					
					// -> radio buttons
					// ----------------

					// Un seul choix possible, on peut définir au début la valeur sélectionnée par défaut
					if (isset($this->d[$id]) && $this->d[$id] != ""){
						// On a une valeur de retour
						$selectionne = $this->d[$id];
					}elseif (isset($this->s[$id]["default"])){
						// On a une valeur par défaut
						$selectionne = $this->s[$id]["default"];
					}else
						$selectionne = "";

					if (isset($this->s[$id]["valeurs"])){
						foreach ($this->s[$id]["valeurs"] as $k => $v){
							echo '<input type="radio" name="'.$id.'" id="'.$k.'" value="'.$k.'"';
							echo (($selectionne == $k) ? ' checked="checked"' : '');
							
							// Regarde si c'est requis ou pas
							echo ((isset($this->s[$id]["required"])) ? ' required="required" ' : "");

							echo '/> <label for="'.$k.'"> '.$v.'</label>';
							echo $this->separateurcr;
						}
					}

				}

				// Fin du fieldset
				echo '</fieldset>';

			break;
			
		}

		echo $this->separateurentre;

	}

}

/*

Exemple d'utilisation
---------------------

$f = new Forms("ajoutercategorie", $_POST);
$f->setSeparateurs("<br /><br />", "<br />");

$f->form($_SERVER["PHP_SELF"]);

$f->entree("emailpro");
echo "<br /><br />";
$f->entree("telephonepro");
echo "<br /><br />";
$f->entree("competences");
echo "<br /><hr /><br />";
$f->entree("commentaire");
echo "<br /><br />";
$f->entree("combo");
echo "<br /><br />";
$f->entree("radio");

echo '<input type="submit" value="envoi !"></form>';
*/
/*
$f = new Forms();

$f->setSpecifications("exosoumettre");

$data = array(
	"titre" => "Mon exercice",
	"motsclef" => "matreyfr",
	"data" => "abc",
	"enonce" => "abc",
	"difficulte" => "abc"
	);

$f->setData($data);

$f->testerRegles();
*/
?>
